﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace HYCodeBase.Algorithm.LRUCache
{
	public class LRUCacheManger<X, Y> : IEnumerable<KeyValuePair<X, Y>>, IEnumerable
	{
		public LRUCacheManger(int inputCapacity)
		{
			if (inputCapacity <= 1)
			{
				throw new ArgumentException();
			}
			this.m_useCache = new Dictionary<X, LRULindNode<X, Y>>();
			this.m_count = 0;
			this.m_capacity = inputCapacity;
			this.m_headNode = new EndNode<X, Y>();
			this.m_tailNode = new EndNode<X, Y>();
			this.m_headNode.PostNode = this.m_tailNode;
			this.m_headNode.PreNode = null;
			this.m_tailNode.PostNode = null;
			this.m_tailNode.PreNode = this.m_headNode;
		}

		public bool IfContainsKey(X inputKey)
		{
			return this.m_useCache.ContainsKey(inputKey);
		}

		public bool TryGet(X inputKey, out Y findValue)
		{
			findValue = default(Y);
			if (!this.IfContainsKey(inputKey))
			{
				return false;
			}
			findValue = this.Get(inputKey);
			return true;
		}

		public Y Get(X inputKey)
		{
			if (!this.IfContainsKey(inputKey))
			{
				throw new ArgumentException();
			}
			LRULindNode<X, Y> lrulindNode = this.m_useCache[inputKey];
			this.MoveToHead(lrulindNode);
			return lrulindNode.ThisValue;
		}

		public void Set(X inputKey, Y inputValue)
		{
			if (this.IfContainsKey(inputKey))
			{
				LRULindNode<X, Y> lrulindNode = this.m_useCache[inputKey];
				this.MoveToHead(lrulindNode);
				lrulindNode.ThisValue = inputValue;
			}
			else
			{
				LRULindNode<X, Y> lrulindNode2 = new LRULindNode<X, Y>();
				lrulindNode2.ThisKey = inputKey;
				lrulindNode2.ThisValue = inputValue;
				this.AddNode(lrulindNode2);
				this.m_useCache.Add(inputKey, lrulindNode2);
				this.m_count++;
				if (this.m_count > this.m_capacity)
				{
					LRULindNode<X, Y> nodeNeedRemove = this.Poptail();
					this.RemoveNodeInManger(nodeNeedRemove);
				}
			}
			this.m_useBackDic = null;
		}

		public void Remove(X inputKey)
		{
			if (!this.IfContainsKey(inputKey))
			{
				return;
			}
			LRULindNode<X, Y> nodeNeedRemove = this.m_useCache[inputKey];
			this.RemoveNodeInManger(nodeNeedRemove);
		}

		public IEnumerator<KeyValuePair<X, Y>> GetEnumerator()
		{
			return this.PrepareEnumeratorDic().GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.PrepareEnumeratorDic().GetEnumerator();
		}

		private Dictionary<X, Y> PrepareEnumeratorDic()
		{
			if (this.m_useBackDic == null)
			{
				this.m_useBackDic = (from n in this.m_useCache
				select new KeyValuePair<X, Y>(n.Key, n.Value.ThisValue)).ToDictionary((KeyValuePair<X, Y> k) => k.Key, (KeyValuePair<X, Y> k) => k.Value);
			}
			return this.m_useBackDic;
		}

		private void RemoveNodeInManger(LRULindNode<X, Y> nodeNeedRemove)
		{
			this.RemoveNodeInLink(nodeNeedRemove);
			this.m_useCache.Remove(nodeNeedRemove.ThisKey);
			this.m_count--;
		}

		private void AddNode(LRULindNode<X, Y> inputNode)
		{
			LRULindNode<X, Y> postNode = this.m_headNode.PostNode;
			this.m_headNode.PostNode = inputNode;
			inputNode.PreNode = this.m_headNode;
			postNode.PreNode = inputNode;
			inputNode.PostNode = postNode;
		}

		private void MoveToHead(LRULindNode<X, Y> inputNode)
		{
			this.RemoveNodeInLink(inputNode);
			this.AddNode(inputNode);
		}

		private void RemoveNodeInLink(LRULindNode<X, Y> inputNode)
		{
			LRULindNode<X, Y> preNode = inputNode.PreNode;
			LRULindNode<X, Y> postNode = inputNode.PostNode;
			inputNode.PreNode = null;
			inputNode.PostNode = null;
			preNode.PostNode = postNode;
			postNode.PreNode = preNode;
		}

		private LRULindNode<X, Y> Poptail()
		{
			return this.m_tailNode.PreNode;
		}

		private Dictionary<X, LRULindNode<X, Y>> m_useCache;

		private Dictionary<X, Y> m_useBackDic;

		private int m_count;

		private int m_capacity;

		private LRULindNode<X, Y> m_headNode;

		private LRULindNode<X, Y> m_tailNode;
	}
}
